package system

import (
	"context"
	"gf_blog_admin/internal/consts"
	"gf_blog_admin/internal/dao"
	"gf_blog_admin/internal/model"
	"gf_blog_admin/internal/service"
	"gf_blog_admin/utility/casbinPolicy"
	"gf_blog_admin/utility/utils"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
)

type sSysRole struct{}

func init() {
	service.RegisterSysRole(NewSysRole())
}

func NewSysRole() *sSysRole {
	return &sSysRole{}
}

func (s *sSysRole) List(ctx context.Context, in *model.RoleListInput) (out []*model.RoleItem, total int, err error) {
	var (
		cls = dao.BlSysRole.Columns()
		m   = dao.BlSysRole.Ctx(ctx)
	)

	out = make([]*model.RoleItem, 0)
	err = m.Fields(cls.Id, cls.RoleName, cls.RoleSlug, cls.GuardName, cls.RoleRemark, cls.RoleStatus).
		Page(in.Page, in.PageSize).
		ScanAndCount(&out, &total, false)
	return
}

func (s *sSysRole) All(ctx context.Context) (out []*model.RoleAllItem, err error) {
	cls := dao.BlSysRole.Columns()
	out = make([]*model.RoleAllItem, 0)
	err = dao.BlSysRole.Ctx(ctx).
		Where(cls.RoleStatus, 1).
		Fields(cls.Id, cls.RoleName, cls.RoleSlug).
		OrderDesc(cls.Id).
		Scan(&out)
	return
}

func (s *sSysRole) Detail(ctx context.Context, id uint64) (role *model.RoleItem, err error) {
	err = dao.BlSysRole.Ctx(ctx).Where(dao.BlSysRole.Columns().Id, id).Scan(&role)
	if err != nil {
		return nil, err
	}
	if role == nil {
		return nil, gerror.New("角色记录不存在")
	}
	return
}

func (s *sSysRole) Store(ctx context.Context, in *model.RoleFormInput) (err error) {
	cls := dao.BlSysRole.Columns()
	if err = utils.IsUnique(ctx, &dao.BlSysRole, map[string]string{
		cls.RoleName: in.RoleName,
	}, "角色名已存在"); err != nil {
		return err
	}

	err = dao.BlSysRole.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.BlSysRole.Ctx(ctx).Data(in).Insert()
		return err
	})
	return
}

func (s *sSysRole) Update(ctx context.Context, id uint64, in *model.RoleFormInput) (err error) {
	cls := dao.BlSysRole.Columns()
	if err = utils.IsUnique(ctx, &dao.BlSysRole, map[string]interface{}{
		cls.Id + " !=": id,
		cls.RoleName:   in.RoleName,
	}, "角色名已存在"); err != nil {
		return err
	}

	if _, err = s.Detail(ctx, id); err != nil {
		return err
	}

	err = dao.BlSysRole.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err = dao.BlSysRole.Ctx(ctx).Where(cls.Id, id).Data(in).Update()
		return err
	})
	return
}

func (s *sSysRole) Destroy(ctx context.Context, ids []uint64) error {
	var (
		roles []string
		cls   = dao.BlSysRole.Columns()
		m     = dao.BlSysRole.Ctx(ctx)
	)

	ids = utils.UniqueIds(ids)
	result, err := m.WhereIn(cls.Id, ids).Fields(cls.RoleName).Array()
	if err != nil {
		return err
	}
	if result == nil || len(result) == 0 {
		return gerror.New("数据记录不存在")
	}
	for _, v := range result {
		va := v.String()
		if va == consts.SuperRole {
			return gerror.New("超管角色禁止删除！")
		}
		roles = append(roles, va)
	}
	err = dao.BlSysRole.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		if _, err = m.WhereIn(cls.Id, ids).Delete(); err != nil {
			return err
		}
		if err = s.delRoles(roles); err != nil {
			return err
		}
		return nil
	})
	return err
}

func (s *sSysRole) Permissions(ctx context.Context, id uint64) (out *model.RolePermissionsOutput, err error) {
	role, ok := s.Detail(ctx, id)
	if ok != nil {
		return nil, ok
	}

	// 角色绑定的所有权限
	out = &model.RolePermissionsOutput{}
	out.Checked = s.getRules(role.RoleName)
	out.Rows, err = service.SysMenu().SelectMenuTree(ctx)
	return
}

func (s *sSysRole) AssignPermissions(ctx context.Context, id uint64, rules []string) error {
	role, err := s.Detail(ctx, id)
	if err != nil {
		return err
	}
	if role.RoleName == consts.SuperRole {
		return gerror.New("超管角色拥有全部权限，无需修改！")
	}
	err = dao.BlSysRole.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		if err = s.detachRule(role.RoleName); err != nil {
			return err
		}
		if err = s.attachRules(role.RoleName, rules); err != nil {
			return err
		}
		return nil
	})
	return err
}

// 获取角色权限
func (s *sSysRole) getRules(role string) []string {
	rules, _ := casbinPolicy.Enforcer.GetPermissionsForUser(role)
	list := make([]string, 0)
	if rules != nil {
		for _, v := range rules {
			if v[1] != "" {
				list = append(list, v[1])
			}
		}
	}
	return list
}

// 添加角色权限
func (s *sSysRole) attachRule(role string, rule string) error {
	if role == "" || rule == "" {
		return nil
	}
	_, err := casbinPolicy.Enforcer.AddPermissionForUser(role, rule, "all")
	return err
}

// 添加多个角色权限
func (s *sSysRole) attachRules(role string, rules []string) error {
	if rules == nil || len(rules) == 0 {
		return nil
	}
	for _, rule := range rules {
		if err := s.attachRule(role, rule); err != nil {
			return err
		}
	}
	return nil
}

// 删除多个角色
func (s *sSysRole) delRoles(roles []string) error {
	for _, role := range roles {
		if _, err := casbinPolicy.Enforcer.DeleteRole(role); err != nil {
			return err
		}
	}
	return nil
}

// 删除角色权限
func (s *sSysRole) detachRule(role string) error {
	_, err := casbinPolicy.Enforcer.DeletePermissionsForUser(role)
	return err
}

// 删除多个角色权限
func (s *sSysRole) detachRules(roles []string) error {
	for _, role := range roles {
		if err := s.detachRule(role); err != nil {
			return err
		}
	}
	return nil
}
